﻿#region COPYRIGHT© 2005-2013 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Text;

namespace FlitBit.Parse
{
	/// <summary>
	///   Exception indicating a parse error has occurred.
	/// </summary>
	[Serializable]
	public class ParseException : FormatException
	{
		/// <summary>
		///   Value indicating an ErrorPosition is unknown or invalid.
		/// </summary>
		public static readonly int InvalidErrorPosition = -1;

		int _position = InvalidErrorPosition;

		/// <summary>
		///   Creates a new instance.
		/// </summary>
		public ParseException()
		{}

		/// <summary>
		///   Creates a new instance initializing the error's message.
		/// </summary>
		/// <param name="msg">the error message</param>
		public ParseException(string msg)
			: base(msg)
		{}

		/// <summary>
		///   Creates a new instance initializing the error's message
		///   and indicating the cursor position of the error.
		/// </summary>
		/// <param name="msg">the error message</param>
		/// <param name="cursor">the cursor position of the error</param>
		public ParseException(string msg, Cursor cursor)
			: base(MakeErrorMessage(msg, cursor))
		{
			_position = cursor;
		}

		/// <summary>
		///   Creates a new instance indicating the cursor position of the error
		///   and the values that were expected at that position.
		/// </summary>
		/// <param name="curs">the cursor position of the error</param>
		/// <param name="expected">expected values</param>
		public ParseException(Cursor curs, params object[] expected)
			: base(MakeErrorMessage(curs, expected))
		{}

		/// <summary>
		///   Position of the parse error within the parse input.
		/// </summary>
		/// <value>Offset from the beginning parse input where the error occurred.</value>
		public int ErrorPosition { get { return _position; } }

		/// <summary>
		/// Makes a parse exception describing a failed expectation.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="curs"></param>
		/// <param name="found"></param>
		/// <param name="expected"></param>
		/// <returns></returns>
		public static ParseException MakeFromExpected<T>(Cursor curs, T found, params T[] expected)
		{
			var buffer = new StringBuilder(400)
				.Append("Input cannot be parsed at ")
				.Append(curs.ToString())
				.Append(". Expected: ");
			for (var i = 0; i < expected.Length; ++i)
			{
				if (i > 0)
				{
					buffer.Append(i == expected.Length - 1 ? " or " : ", ");
				}
				buffer.Append(Convert.ToString(expected[i]));
			}
			buffer.Append(". Found: ")
						.Append(Convert.ToString(found));
			return new ParseException(buffer.ToString());
		}

		/// <summary>
		/// Makes a parse exception describing a failed expectation.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="expected"></param>
		/// <returns></returns>
		public static ParseException MakeFromExpected<T>(params T[] expected)
		{
			var buffer = new StringBuilder(400)
				.Append("Input cannot be parsed: expected ");
			for (var i = 0; i < expected.Length; ++i)
			{
				if (i > 0)
				{
					if (i == expected.Length - 1)
					{
						buffer.Append(" or ");
					}
					else
					{
						buffer.Append(", ");
					}
				}
				buffer.Append(Convert.ToString(expected[i]));
			}
			return new ParseException(buffer.ToString());
		}

		static string MakeErrorMessage(string message, Cursor curs)
		{
			return String.Concat(message, " - at ", curs);
		}

		static string MakeErrorMessage(Cursor curs, params object[] expected)
		{
			var buffer = new StringBuilder(400)
				.Append("Input cannot be parsed at ")
				.Append(curs.ToString())
				.Append(": expected ");
			for (var i = 0; i < expected.Length; ++i)
			{
				if (i > 0)
				{
					if (i == expected.Length - 1)
					{
						buffer.Append(" or ");
					}
					else
					{
						buffer.Append(", ");
					}
				}
				buffer.Append(Convert.ToString(expected[i]));
			}
			return buffer.ToString();
		}
	}
}